package com.pascuit.gpxhandler.server.service;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;

import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.stereotype.Service;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreInputStream;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.pascuit.gpxhandler.client.GpxService;
import com.pascuit.gpxhandler.server.dao.UserDao;
import com.pascuit.gpxhandler.shared.dto.GpxInfoDto;
import com.pascuit.gpxhandler.shared.dto.LatLngDto;
import com.pascuit.gpxhandler.shared.generated.jaxb2.Gpx;
import com.pascuit.gpxhandler.shared.generated.jaxb2.TrkType;
import com.pascuit.gpxhandler.shared.generated.jaxb2.TrksegType;
import com.pascuit.gpxhandler.shared.generated.jaxb2.WptType;

/**
 * 
 * @author Parant
 * 
 */
@Service("gpxService")
public class GpxServiceImpl implements GpxService {

	@Autowired
	private Unmarshaller jaxb2Unmarshaller;
	@Autowired
	private Marshaller jaxb2Marshaller;
	@Autowired
	private UserDao userDao;

	BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

	public Gpx loadGpxWithJaxb2(BlobKey blobKey) throws IOException {
		BlobstoreInputStream is = null;
		try {
			is = new BlobstoreInputStream(blobKey);
			return (Gpx) this.jaxb2Unmarshaller.unmarshal(new StreamSource(is));
		} finally {
			if (is != null) {
				is.close();
			}
		}
	}

	public void convertFromObjectToXMLWithJaxb2(Object object, OutputStream os) throws IOException {
		try {
			jaxb2Marshaller.marshal(object, new StreamResult(os));
		} finally {
			if (os != null) {
				os.close();
			}
		}
	}

	public GpxInfoDto populateGpxInfo(GpxInfoDto gpxInfo) {
		BlobKey blobKey = new BlobKey(gpxInfo.getBlogKey());

		Gpx jaxb2Gpx = null;
		try {
			jaxb2Gpx = loadGpxWithJaxb2(blobKey);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}

		if (jaxb2Gpx.getMetadata() != null) {
			GregorianCalendar calendar = jaxb2Gpx.getMetadata().getTime().toGregorianCalendar();
			gpxInfo.setStartDate(calendar.getTime());
		}

		List<LatLngDto> points = new LinkedList<LatLngDto>();
		if (!jaxb2Gpx.getTrks().isEmpty()) {
			// get the first point
			TrkType trkType = jaxb2Gpx.getTrks().get(0);
			gpxInfo.setName(trkType.getName());
			List<TrksegType> segments = trkType.getTrksegs();
			if (!segments.isEmpty()) {
				for (TrksegType trksegType : segments) {
					List<WptType> trks = trksegType.getTrkpts();
					// dans tous les cas on ajoute l'element dans la liste
					for (WptType wptType : trks) {
						LatLngDto latLng = new LatLngDto();
						latLng.setLat(wptType.getLat().doubleValue());
						latLng.setLng(wptType.getLon().doubleValue());

						GregorianCalendar timeGregorianCalendar = wptType.getTime().toGregorianCalendar();
						latLng.setDate(timeGregorianCalendar.getTime());
						// gpxInfo.setGpsStartDate(timeGregorianCalendar.getTime());

						// If no start date in the metadata then start date
						// is first point
						if (gpxInfo.getStartDate() == null) {
							gpxInfo.setStartDate(timeGregorianCalendar.getTime());
						}

						// get the last point
						WptType lastPt = trks.get(trks.size() - 1);
						gpxInfo.setGpsEndDate(lastPt.getTime().toGregorianCalendar().getTime());

						points.add(latLng);
					}
				}
			}

			if (!points.isEmpty()) {
				gpxInfo.setGpsStartDate(points.get(0).getDate());
				gpxInfo.setGpsEndDate(points.get(points.size() - 1).getDate());
			}
		}

		gpxInfo.setLatLng(points);

		userDao.saveOrUpdate(gpxInfo);

		gpxInfo.getUserDto().setActivities(new ArrayList<GpxInfoDto>(gpxInfo.getUserDto().getActivities()));

		return gpxInfo;
	}

	public Date getGpsStartDate(List<TrkType> trkTypes) {
		for (TrkType trkType : trkTypes) {
			for (TrksegType trkSeg : trkType.getTrksegs()) {
				WptType wptType = trkSeg.getTrkpts().get(0);
				return wptType.getTime().toGregorianCalendar().getTime();
			}
		}
		return null;
	}

	public void cutFile(Gpx gpx, long startTime, long endTime) {
		long startMillis = startTime * 1000;
		long endMillis = endTime * 1000;

		if (!gpx.getTrks().isEmpty()) {
			TrkType trkType = gpx.getTrks().get(0);

			List<TrksegType> segments = trkType.getTrksegs();
			if (!segments.isEmpty()) {
				TrksegType firstSeg = segments.get(0);
				List<WptType> trks = firstSeg.getTrkpts();

				List<WptType> newList = new ArrayList<WptType>(trks.size());
				for (WptType wptType : trks) {
					long time = wptType.getTime().toGregorianCalendar().getTimeInMillis();

					if ((time >= startMillis && time <= endMillis)) {
						newList.add(wptType);
					}
				}
				firstSeg.setTrkpts(newList);
			}
		}
	}
}
